package web

import (
	"context"
	"github.com/ecodeclub/ekit/slice"
	"github.com/gin-gonic/gin"
	"golang.org/x/sync/errgroup"
	"net/http"
	"strconv"
	"time"
	intrv1 "webook/api/proto/gen/intr/v1"
	rewardv1 "webook/api/proto/gen/reward/v1"
	"webook/internal/domain"
	"webook/internal/service"
	"webook/internal/web/jwt"
	"webook/pkg/ginx"
	"webook/pkg/logger"
)

type ArticleHandler struct {
	svc         service.ArticleService
	l           logger.LoggerV1
	reward      rewardv1.RewardServiceClient
	interactSvc intrv1.InteractiveServiceClient
	biz         string
}

func NewArticleHandler(l logger.LoggerV1, svc service.ArticleService,
	interactSvc intrv1.InteractiveServiceClient, reward rewardv1.RewardServiceClient) *ArticleHandler {
	return &ArticleHandler{
		svc: svc, l: l,
		interactSvc: interactSvc,
		biz:         "article",
		reward:      reward,
	}
}

func (h *ArticleHandler) RegisterRoutes(server *gin.Engine) {
	g := server.Group("/articles")
	g.POST("/edit", h.Edit)
	g.POST("/publish", h.Publish)
	g.POST("/withdraw", h.Withdraw)

	//创作者接口
	g.GET("/detail/:id", h.Detail)
	//理论上应该用get方法，但是类型转化很麻烦 所以用post 方便
	g.POST("/list", h.List)
	//g.POST("/pub/detail",h.PubDetail)
	pub := g.Group("/pub")
	pub.GET("/:id", h.PubDetail)
	//传入参数 true为点赞 false为不点赞
	pub.POST("/like", h.Like)
	//pub.GET("/cancel_like",h.)
	pub.POST("/collect", h.Collect)
	pub.POST("/reward",
		ginx.WrapBodyAndClaims[ArticleRewardReq, jwt.UserClaims](h.Reward))
}

// 假设返回文章的id
func (h *ArticleHandler) Edit(ctx *gin.Context) {
	type Req struct {
		Id      int64  `json:"id"`
		Title   string `json:"title"`
		Content string `json:"content"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}
	uc := ctx.MustGet("user").(jwt.UserClaims)
	artId, err := h.svc.Save(ctx, domain.Article{
		Id:      req.Id,
		Title:   req.Title,
		Content: req.Content,
		Author: domain.Author{
			Id: uc.Uid,
		},
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("保存文章数据失败", logger.Int64("uid", uc.Uid), logger.Error(err))
	}
	ctx.JSON(http.StatusOK, Result{
		Data: artId,
	})
}

func (h *ArticleHandler) Publish(ctx *gin.Context) {
	type Req struct {
		Id      int64  `json:"id"`
		Title   string `json:"title"`
		Content string `json:"content"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}
	uc := ctx.MustGet("user").(jwt.UserClaims)
	artId, err := h.svc.Publish(ctx, domain.Article{
		Id:      req.Id,
		Title:   req.Title,
		Content: req.Content,
		Author: domain.Author{
			Id: uc.Uid,
		},
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("发表文章数据失败", logger.Int64("uid", uc.Uid), logger.Error(err))
	}
	ctx.JSON(http.StatusOK, Result{
		Data: artId,
	})
}

func (h *ArticleHandler) Withdraw(ctx *gin.Context) {
	type Req struct {
		Id int64 `json:"id"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		return
	}
	uc := ctx.MustGet("user").(jwt.UserClaims)
	err := h.svc.Withdraw(ctx, uc.Uid, req.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("撤回文章失败", logger.Int64("uid", uc.Uid), logger.Int64("aid", req.Id),
			logger.Error(err))
	}
	ctx.JSON(http.StatusOK, Result{
		Code: 200,
		Msg:  "success",
	})
}

func (h *ArticleHandler) Detail(ctx *gin.Context) {
	idstr := ctx.Param("id")
	id, err := strconv.ParseInt(idstr, 10, 64)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "参数错误",
		})
	}
	art, err := h.svc.GetById(ctx, id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("查询文章失败", logger.Int64("id", id), logger.Error(err))
		return
	}
	uc := ctx.MustGet("user").(jwt.UserClaims)
	if art.Author.Id != uc.Uid {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("非法查询文章", logger.Int64("id", id), logger.Int64("uc_id", uc.Uid),
			logger.Int64("author_id", art.Author.Id), logger.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Data: ArticleVo{
			Id:     art.Id,
			Title:  art.Title,
			Status: art.Status.ToUint8(),
			//Abstract: art.Abstract(),
			Content: art.Content,
			// 这个是创作者看自己的文章列表，也不需要这个字段
			//AuthorId: art.Author.Id,
			Ctime: art.Ctime.Format(time.DateTime),
			Utime: art.Utime.Format(time.DateTime),
		},
	})
}

func (h *ArticleHandler) List(ctx *gin.Context) {
	type Req struct {
		Offset int `json:"offset"`
		Limit  int `json:"limit"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		h.l.Error("反序列化请求失败", logger.Error(err))
		return
	}
	uc := ctx.MustGet("user").(jwt.UserClaims)
	arts, err := h.svc.GetByAuthor(ctx, uc.Uid, req.Offset, req.Limit)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("根据作者id查询文章列表失败", logger.Int64("uid", uc.Uid), logger.Int("offset", req.Offset),
			logger.Int("limit", req.Limit), logger.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Data: slice.Map[domain.Article, ArticleVo](arts,
			func(idx int, src domain.Article) ArticleVo {
				return ArticleVo{
					Id:       src.Id,
					Title:    src.Title,
					Status:   src.Status.ToUint8(),
					Abstract: src.Abstract(),
					// 这个列表请求，不需要返回内容
					//Content: src.Content,
					// 这个是创作者看自己的文章列表，也不需要这个字段
					AuthorId: src.Author.Id,
					Ctime:    src.Ctime.Format(time.DateTime),
					Utime:    src.Utime.Format(time.DateTime),
				}
			}),
	})
}

func (h *ArticleHandler) PubDetail(ctx *gin.Context) {
	idstr := ctx.Param("id")
	id, err := strconv.ParseInt(idstr, 10, 64)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 4,
			Msg:  "参数错误",
		})
		return
	}
	var (
		eg   errgroup.Group
		art  domain.Article
		intr *intrv1.GetResponse
	)
	eg.Go(func() error {
		var er error
		uc := ctx.MustGet("user").(jwt.UserClaims)
		art, er = h.svc.GetPubById(ctx, id, uc.Uid)
		return er
	})
	eg.Go(func() error {
		uc := ctx.MustGet("user").(jwt.UserClaims)
		var er error
		intr, er = h.interactSvc.Get(ctx, &intrv1.GetRequest{
			Biz: h.biz, BizId: id, Uid: uc.Uid,
		})
		return er
	})

	err = eg.Wait()

	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("查询文章失败", logger.Int64("aid", id), logger.Error(err))
		return
	}
	go func() {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		_, er := h.interactSvc.IncrReadCnt(ctx, &intrv1.IncrReadCntRequest{
			Biz: h.biz, BizId: art.Id,
		})
		if er != nil {
			//打印日志
		}
	}()

	ctx.JSON(http.StatusOK, Result{
		Data: ArticleVo{
			Id:         art.Id,
			Title:      art.Title,
			Status:     art.Status.ToUint8(),
			Content:    art.Content,
			AuthorId:   art.Author.Id,
			AuthorName: art.Author.Name,
			Ctime:      art.Ctime.Format(time.DateTime),
			Utime:      art.Utime.Format(time.DateTime),
			ReadCnt:    intr.Intr.ReadCnt,
			LikeCnt:    intr.Intr.LikeCnt,
			CollectCnt: intr.Intr.CollectCnt,
			Liked:      intr.Intr.Liked,
			Collected:  intr.Intr.Collected,
		},
	})
}

func (h *ArticleHandler) Like(ctx *gin.Context) {
	type Req struct {
		Id int64 `json:"id"`
		//true是点赞 false是不点赞
		Like bool `json:"like"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		h.l.Error("反序列化请求失败", logger.Error(err))
		return
	}
	uc := ctx.MustGet("user").(jwt.UserClaims)
	var err error
	if req.Like {
		_, err = h.interactSvc.Like(ctx, &intrv1.LikeRequest{
			Biz: h.biz, BizId: req.Id, Uid: uc.Uid,
		})
	} else {
		_, err = h.interactSvc.CancelLike(ctx, &intrv1.CancelLikeRequest{
			Biz: h.biz, BizId: req.Id, Uid: uc.Uid,
		})
	}
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("点赞/取消点赞失败", logger.Int64("aid", req.Id),
			logger.Int64("uid", uc.Uid), logger.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "OK",
	})
}

func (h *ArticleHandler) Collect(ctx *gin.Context) {
	type Req struct {
		Id int64 `json:"id"`
		//收藏夹id
		Cid int64 `json:"cid"`
	}
	var req Req
	if err := ctx.Bind(&req); err != nil {
		h.l.Error("反序列化请求失败", logger.Error(err))
		return
	}
	uc := ctx.MustGet("user").(jwt.UserClaims)
	_, err := h.interactSvc.Collect(ctx, &intrv1.CollectRequest{
		Biz: h.biz, BizId: req.Id, Cid: req.Cid, Uid: uc.Uid,
	})
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		h.l.Error("收藏点赞失败", logger.Int64("aid", req.Id),
			logger.Int64("uid", uc.Uid), logger.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "OK",
	})
}

func (h *ArticleHandler) Reward(ctx *gin.Context, req ArticleRewardReq, uc jwt.UserClaims) (ginx.Result, error) {
	// 在这里分发
	// h.reward.WechatPreReward
	// h.reward.AlipayPreReward
	artResp, err := h.svc.GetPubById(ctx, req.Id, uc.Uid)
	if err != nil {
		return ginx.Result{Msg: "系统错误"}, err
	}
	// 最关键的一步骤，就是拿到二维码
	resp, err := h.reward.PreReward(ctx, &rewardv1.PreRewardRequest{
		Biz:       "article",
		BizId:     artResp.Id,
		BizName:   artResp.Title,
		TargetUid: artResp.Author.Id,
		Uid:       uc.Uid,
		Amt:       req.Amt,
	})
	if err != nil {
		return ginx.Result{Msg: "系统错误"}, err
	}
	return ginx.Result{
		Data: map[string]any{
			"codeURL": resp.CodeUrl,
			"rid":     resp.Rid,
		},
	}, nil
}
